package testing;

import static org.junit.Assert.assertTrue;
import hypeerweb.HyPeerWeb;
import hypeerweb.Node;
import hypeerweb.SimplifiedNodeDomain;
import hypeerweb.WebId;

import java.util.ArrayList;

import org.junit.Test;

import visitor.FindVisitor;
import visitor.Parameters;
import visitor.SendVisitor;
import visitor.TrackSendPathVisitor;

public class ChaseTests {
    public static HyPeerWeb hypeerweb = null;

    @Test
    public void test() {
        hypeerweb = HyPeerWeb.getSingleton();
        // – Skip loop entirely
        // – Exactly one pass
        // – Exactly two passes
        // – N-1, N, and N+1 passes [N is the maximum number of passes]
        // – M passes, where 2 < M < N-1
        assertTrue(loopTest());
        // For each compound condition, C, test the True and False branches of C
        // and
        // every sub-expression (simple or not) within C, but not all possible
        // combinations
        assertTrue(relationalTesting());
        // Look for boundary conditions in the code, and create test cases for
        // boundary – 1, boundary, boundary + 1
        assertTrue(internalBoundary());
        // – Determine all possible places in the program where the variable
        // could have been defined (i.e., given its most recent value)
        // – Create a test case for each possible (Definition, Use) pair
        assertTrue(dataflowTesting());

    }

    private boolean dataflowTesting() {
        // TODO dist - used to keep track of the shortest distance between two
        // nodes.
        // closest - the current closest node to the target node.
        // target - node we are trying to send to. Depending on its use,
        // broadcast declares it to be an array of webId's
        // send uses the method as an array of integers thus they need to be
        // cast to webIds appropriately.

        // target interacts with dist when it gets the appropriate dist from
        // itself to the node it is visiting in the hw.
        // -it does this in 3 different places.
        // 1.) where it is not the node you are looking for, so it sets the
        // distance between them.
        // 2.) when it checks the fold. (for speed)
        // 3.) when it checks the all the other connections.
        // target interacts with closest when the node that is found is actually
        // closer to it that the original closest.
        // -it does this is in 2 different places along with an initialization
        // of node to null.
        // 1.) when it checks the fold. (for speed)
        // 2.) when it checks the all the other connections.
        // tests the flow of the visitor through the hypeerweb.
      
        for (int i = 0; i < sizes.length; i++) {
            int hypeerwebSize = sizes[i];
            int target = targets[i];
            boolean testSendError = testSend(hypeerwebSize, target);
            if (testSendError) {
                System.out.println("Error in DataFlow: i=" + i);
                return false;
            }
        }
        return true;
    }

    private boolean internalBoundary() {
        // TODO Auto-generated method stub
        createHyPeerWebWith(32);
        Node node0 = hypeerweb.getNode(0);
        FindVisitor fVisit = new FindVisitor();
        // outside
        Parameters params = fVisit.createInitialParameters(new WebId(32));
        node0.accept(fVisit, params);
        if ((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null) {
            return false;
        }
        // on boundary
        params = fVisit.createInitialParameters(new WebId(31));
        node0.accept(fVisit, params);
        if ((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null) {
            return false;
        }
        // within
        params = fVisit.createInitialParameters(new WebId(30));
        node0.accept(fVisit, params);
        if ((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null) {
            return false;
        }
        // before boundary
        params = fVisit.createInitialParameters(new WebId(-1));
        node0.accept(fVisit, params);
        if ((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null) {
            return false;
        }
        // at boundary
        params = fVisit.createInitialParameters(new WebId(0));
        node0.accept(fVisit, params);
        if ((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null) {
            return false;
        }
        // within
        params = fVisit.createInitialParameters(new WebId(1));
        node0.accept(fVisit, params);
        if ((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null) {
            return false;
        }
        return true;
    }

    private boolean relationalTesting() {
        createHyPeerWebWith(32);
        Node node0 = hypeerweb.getNode(0);
        FindVisitor fVisit = new FindVisitor();
        // The parameter is either storing an Integer or a WebId
        // outside
        Parameters params = new Parameters();
        params.set(FindVisitor.TARGET_KEY, 13);
        node0.accept(fVisit, params);
        if (!(params.get(FindVisitor.TARGET_KEY) instanceof Integer)) {
            return false;
        }
        // check that if we arrived at the target node or not.
        params = fVisit.createInitialParameters(new WebId(0));
        node0.accept(fVisit, params);
        if ((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null) {
            return false;
        }
        // check the fold for a closer connection or not

        // we didn't find any closer or not.
        // -we arrived at node.

        // -we didn't arrive at node.
        return true;
    }

    public boolean loopTest() {
        // visit only has one loop in it, that would be the delegates. This is a
        // for each loop so it is reliable in
        // boundaries so I will check different cases when there is no
        // connections to go through, when there is one,
        // when there is many.
        createHyPeerWebWith(1);
        Node node0 = hypeerweb.getNode(0);
        FindVisitor fVisit = new FindVisitor();
        // outside
        Parameters params = fVisit.createInitialParameters(new WebId(0));
        node0.accept(fVisit, params);
        if ((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null) {
            return false;
        }
        createHyPeerWebWith(2);
        node0= hypeerweb.getNode(0);
        if ((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null) {
            return false;
        }
        return true;
    }

    static private void createHyPeerWebWith(int numberOfNodes) {
        hypeerweb.clear();
        Node node0 = new Node(0);
        hypeerweb.addToHyPeerWeb(node0, null);

        for (int i = 1; i < numberOfNodes; i++) {
            Node node = new Node(0);
            node0.addToHyPeerWeb(node);
        }
    }

    private static boolean testSend(int hypeerwebSize, int target) {
        createHyPeerWebWith(hypeerwebSize);

        for (int i = 0; i < hypeerwebSize; i++) {
            TrackSendPathVisitor trackSendPathVisitor = new TrackSendPathVisitor();
            Parameters parameters = new Parameters();
            parameters.set(SendVisitor.TARGET_KEY, target);
            ArrayList<Integer> emptyList = new ArrayList<Integer>();
            parameters.set(TrackSendPathVisitor.PATH_KEY, emptyList);
            hypeerweb.getNode(i).accept(trackSendPathVisitor, parameters);
        }
        ArrayList<ArrayList<Integer>> finalResult = (ArrayList<ArrayList<Integer>>) hypeerweb
                .getNode(target).getContents()
                .get(TrackSendPathVisitor.PATHS_KEY);
        return checkFinalResult1(finalResult, target);
    }

    private static boolean checkFinalResult1(
            ArrayList<ArrayList<Integer>> paths, int target) {
        boolean result = false;
        for (ArrayList<Integer> path : paths) {
            int loc = checkFinalResult(path, target);
            if (loc != -1)
                return true;

        }
        return result;
    }

    private static int checkFinalResult(ArrayList<Integer> path, int targetId) {
        int result = -1;
        for (int i = 0; result == -1 && i < path.size() - 1; i++) {
            if (!isBestChoice(path.get(i), path.get(i + 1), targetId)) {
                result = i;
            }
        }
        return result;
    }

    private static boolean isBestChoice(int source, int nextNode, int targetId) {
        SimplifiedNodeDomain sourceNode = hypeerweb.getNode(source)
                .constructSimplifiedNodeDomain();
        boolean result = !sourceNode.containsCloserNode(nextNode, targetId);
        return result;
    }

    private static int[] sizes = { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 10, 53, 39,
            46, 67, 75, 25, 85, 43, 86, 61, 127, 75, 84, 34, 21, 113, 7, 77,
            65, 70, 41, 69, 114, 128, 91, 96, 38, 31, 48, 21, 113, 101, 100,
            42, 21, 47, 71, 100, 114 };

    private static int[] targets = { 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 4, 14, 15,
            1, 60, 50, 5, 51, 35, 71, 41, 121, 58, 5, 1, 0, 108, 1, 74, 60, 4,
            20, 44, 63, 54, 70, 27, 27, 4, 14, 9, 46, 66, 12, 17, 13, 18, 39,
            44, 107 };

}
